/*
	Adrenaline
	Copyright (C) 2016-2018, TheFloW

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <psp2/appmgr.h>
#include <psp2/ctrl.h>
#include <psp2/sysmodule.h>
#include <psp2/io/fcntl.h>
#include <psp2/io/devctl.h>
#include <psp2/io/dirent.h>
#include <psp2/io/stat.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/modulemgr.h>
#include <psp2/net/http.h>
#include <psp2/net/net.h>
#include <psp2/net/netctl.h>
#include <taihen.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utils.h"
#include "pspdebug.h"

#include "modules/adrenaline_compat.h"

#define printf psvDebugScreenPrintf

#define EBOOT_URL "http://de01.psp.update.playstation.org/update/psp/image/eu/2014_1212_6be8878f475ac5b1a499b95ab2f7d301/EBOOT.PBP"
#define ADRENALINE_USER_AGENT "Adrenaline/1.00 libhttp/1.1"

static int suspend_locked = 0;

static int powerTickThreadFunc(SceSize args, void *argp)
{
	while (1)
	{
		if (suspend_locked)
			sceKernelPowerTick(SCE_KERNEL_POWER_TICK_DISABLE_AUTO_SUSPEND);

		sceKernelDelayThread(10 * 1000 * 1000);
	}

	return 0;
}

void InitPowerTickThread()
{
	SceUID thid = sceKernelCreateThread("powerTickThreadFunc", powerTickThreadFunc, 0x10000100, 0x40000, 0, 0, NULL);
	if (thid >= 0)
		sceKernelStartThread(thid, 0, NULL);
}

void LockSuspend()
{
	suspend_locked = 1;
}

void UnlockSuspend()
{
	suspend_locked = 0;
}

static int downloadFile(const char *src, const char *dst)
{
	int res;
	int statusCode;
	int tmplId = -1, connId = -1, reqId = -1;
	SceUID fd = -1;
	int ret = 1;

	res = sceHttpCreateTemplate(ADRENALINE_USER_AGENT, SCE_HTTP_VERSION_1_1, SCE_TRUE);
	if (res < 0)
		goto ERROR_EXIT;

	tmplId = res;

	res = sceHttpCreateConnectionWithURL(tmplId, src, SCE_TRUE);
	if (res < 0)
		goto ERROR_EXIT;

	connId = res;

	res = sceHttpCreateRequestWithURL(connId, SCE_HTTP_METHOD_GET, src, 0);
	if (res < 0)
		goto ERROR_EXIT;

	reqId = res;

	res = sceHttpSendRequest(reqId, NULL, 0);
	if (res < 0)
		goto ERROR_EXIT;

	res = sceHttpGetStatusCode(reqId, &statusCode);
	if (res < 0)
		goto ERROR_EXIT;

	if (statusCode == 200)
	{
		uint8_t buf[4096];
		uint64_t size = 0;
		uint32_t value = 0;

		res = sceHttpGetResponseContentLength(reqId, &size);
		if (res < 0)
			goto ERROR_EXIT;

		res = sceIoOpen(dst, SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
		if (res < 0)
			goto ERROR_EXIT;

		fd = res;

		printf("正在下载...");

		int x = psvDebugScreenGetX();
		int y = psvDebugScreenGetY();

		while (1)
		{
			int read = sceHttpReadData(reqId, buf, sizeof(buf));

			if (read < 0)
			{
				res = read;
				break;
			}

			if (read == 0)
				break;

			int written = sceIoWrite(fd, buf, read);

			if (written < 0)
			{
				res = written;
				break;
			}

			value += read;

			psvDebugScreenSetXY(x, y);
			printf("%d%%", (value * 100) / (uint32_t)size);
		}
		printf("\n");
	}

ERROR_EXIT:
	if (fd >= 0)
		sceIoClose(fd);

	if (reqId >= 0)
		sceHttpDeleteRequest(reqId);

	if (connId >= 0)
		sceHttpDeleteConnection(connId);

	if (tmplId >= 0)
		sceHttpDeleteTemplate(tmplId);

	if (res < 0)
		return res;

	return ret;
}

static void initNet()
{
	static char memory[16 * 1024];

	sceSysmoduleLoadModule(SCE_SYSMODULE_NET);
	sceSysmoduleLoadModule(SCE_SYSMODULE_HTTPS);

	SceNetInitParam param;
	param.memory = memory;
	param.size = sizeof(memory);
	param.flags = 0;

	sceNetInit(&param);
	sceNetCtlInit();

	sceHttpInit(40 * 1024);
}

int main()
{
	int res;
	char titleid[12];
	char work_dir[256];
	char flash0_dir[1024];
	int flash0_exist = 0;
	SceIoStat stat;

	psvDebugScreenInit();

	// Safe mode
	if (sceIoDevctl("ux0:", 0x3001, NULL, 0, NULL, 0) == 0x80010030)
	{
		printf("使用本软件前请先在Henkaku设置里开启启用不安全自制软件。");
		while (1)
			;
	}

	// Get titleid
	memset(titleid, 0, sizeof(titleid));
	sceAppMgrAppParamGetString(sceKernelGetProcessId(), 12, titleid, sizeof(titleid));

	// Get directory
	if (strncmp(titleid, "NPXS10007", 9) == 0)
		snprintf(work_dir, sizeof(work_dir), "pd0:app/%s", titleid);
	else if (strncmp(titleid, "NPXS", 4) == 0)
		snprintf(work_dir, sizeof(work_dir), "vs0:app/%s", titleid);
	else
		snprintf(work_dir, sizeof(work_dir), "ux0:app/%s", titleid);

	snprintf(flash0_dir, sizeof(flash0_dir), "%s/flash0", work_dir);
	flash0_exist = (sceIoGetstat(flash0_dir, &stat) >= 0);
	if (!flash0_exist)
	{
		snprintf(flash0_dir, sizeof(flash0_dir), "%s/flash0", PSP_ROOT_DIR);
		flash0_exist = (sceIoGetstat(flash0_dir, &stat) >= 0);
	}

	// Enable write access
	// sceAppMgrUmount("app0:");

	if (!flash0_exist && sceIoGetstat(PSP_ROOT_DIR "/661.PBP", &stat) < 0)
	{
		printf("PSP 6.61固件未安装且661.PBP文件不存在。\n");
		printf("按×键下载PSP 6.61固件。\n");
		printf("按其它任意键忽略 (但你需要手动将661.PBP放置于" PSP_ROOT_DIR "/661.PBP)。\n\n");

		createFolder(PSP_ROOT_DIR);

		int completed = 0;

		while (1)
		{
			SceCtrlData pad;
			sceCtrlReadBufferPositive(0, &pad, 1);

			if (pad.buttons & SCE_CTRL_CROSS)
			{
				initNet();
				InitPowerTickThread();
				LockSuspend();

				res = downloadFile(EBOOT_URL, "ux0:temp.PBP");
				if (res < 0)
				{
					sceIoRemove("ux0:temp.PBP");
					UnlockSuspend();
					printf("下载文件出错: 0x%08X\n", res);
					while (1)
						;
				}
				else
				{
					res = sceIoRename("ux0:temp.PBP", PSP_ROOT_DIR "/661.PBP");
					UnlockSuspend();
					if (res >= 0)
						completed = 1;
				}

				break;
			}
			else if (pad.buttons != 0)
			{
				break;
			}
		}

		if (!completed)
		{
			sceIoRemove("ux0:temp.PBP");
			sceKernelExitProcess(0);
			return 0;
		}
	}

	// Load kernel module
	char kernel_path[1024];
	snprintf(kernel_path, sizeof(kernel_path), "%s/sce_module/adrenaline_kernel.skprx", work_dir);
	res = taiLoadStartKernelModule(kernel_path, 0, NULL, 0);
	if (res < 0)
	{
		printf("无法加载adrenaline_kernel.skprx，请先卸载旧的Adrenaline或尝试重启主机");
		while (1)
			;
	}

	sceKernelExitProcess(0);
	return 0;
}